package com.ld.common.aop;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.ld.common.util.LogUtil;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.util.StringUtils;
import com.ld.shieldsb.common.web.model.SysUser;
import com.ld.shieldsb.common.web.util.Web;

/**
 * 日志切面
 * 
 * @ClassName LogAspect
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2018年6月14日 上午11:04:55
 *
 */
@Aspect
@Order(value = 1) // 值越小的 aspect 越先执行,也可实现org.springframework.core.Ordered接口，实现它的getOrder()方法
@Component
public class AdminLogAspect extends BasicAop {
    // 匹配com.ld.admin(.xxxx).controller包及其子包(..)下的所有类的所有方法
    // execution(方法修饰符(可选) 返回类型 方法名 参数 异常模式(可选)) 并且排除 有NoLog注解的
    @Pointcut("execution(public * com.ld.admin..controller..*.*(..)) && !@annotation(com.ld.shieldsb.annotation.NoLog)")
    public void commonAccessLog() {
    }

    // 错误控制类，用于排除
    @Pointcut("execution(* com.ld.common.MainErrorController.*(..))")
    private void error() {
    }

    // 业务日志查看，用于排除，否则查看业务记录也记录就很多了
    @Pointcut("execution(* com.ld.admin.controller.system.BizLogController.*(..)) || execution(* com.ld.admin.controller.usercenter.UserOperationLogController.*(..))")
    private void bizLog() {
    }

//    @Before("commonAccessLog()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
        outLog("我是前置通知!!!");
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        outLog("URL : " + request.getRequestURL().toString());
        outLog("HTTP_METHOD : " + request.getMethod());
        outLog("IP : " + request.getRemoteAddr());
        outLog("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        outLog("ARGS : " + Arrays.toString(joinPoint.getArgs()));

        // 获取目标方法的参数信息
        Object[] obj = joinPoint.getArgs();
        // AOP代理类的信息
        joinPoint.getThis();
        // 代理的目标对象
        joinPoint.getTarget();
        // 用的最多 通知的签名
        Signature signature = joinPoint.getSignature();
        // 代理的是哪一个方法
        outLog(signature.getName());
        // AOP代理类的名字
        outLog(signature.getDeclaringTypeName());
        // AOP代理类的类（class）信息
        signature.getDeclaringType();
        // 获取RequestAttributes
//        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
//        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);  
        // 如果要获取Session信息的话，可以这样写：
        // HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
        Enumeration<String> enumeration = request.getParameterNames();
        Map<String, String> parameterMap = Maps.newHashMap();
        while (enumeration.hasMoreElements()) {
            String parameter = enumeration.nextElement();
            parameterMap.put(parameter, request.getParameter(parameter));
        }
        String str = JSON.toJSONString(parameterMap);
        if (obj.length > 0) {
            outLog("请求的参数信息为：" + str);
        }

    }

    /**
     * 
     * returning表示接收到的对象
     * 
     * @Title doAfterReturning
     * @author 吕凯
     * @date 2019年1月21日 下午5:18:09
     * @param ret
     * @throws Throwable
     *             void
     */
    @AfterReturning(returning = "ret", pointcut = "commonAccessLog() && !error() && !bizLog()")
    public void doAfterReturning(JoinPoint joinPoint, Object ret) throws Throwable {
        outLog("方法的返回值 : " + ret);
        if (ret instanceof String && ret != null) {
            String retStr = ((String) ret);
            if (retStr.contains("forward:") || retStr.contains("redirect:")) {
                return; // 跳转重定向不记录
            }
        }
        getJoinPointInfo(joinPoint, ret);
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String servletPath = request.getServletPath();
        if (servletPath.endsWith("toSearch") || servletPath.endsWith("toUpdate") || servletPath.endsWith("toAdd")
                || servletPath.endsWith("toAudit") || servletPath.endsWith("toAddOrUpdate")) {
            return; // 跳转页面不记录
        }
        Result result = Web.Request.getWebDoResult(request); // 优先从request中取值
        String msg = "";
        if (result == null && ret instanceof Result) { // 返回结果为json格式
            result = (Result) ret;
        }
        if (result != null) {
            msg = result.getMessage();
            Object resultObj = result.getData();
            if (recordLogByResultObj(request, msg, null, resultObj)) {
                return;
            }
        }
        // 如果msg为空，则获取方法上的注解
        if (StringUtils.isEmpty(msg)) {
            msg = getMethodDescription(joinPoint);
        }
        // 处理完请求，返回内容
        LogUtil.saveLogbyDB(servletPath, msg, request);
    }

    /**
     * 日志查看记录到日志中
     * 
     * @Title doAfterReturningBizLog
     * @author 吕凯
     * @date 2019年2月19日 下午1:52:15
     * @param ret
     * @throws Throwable
     *             void
     */
    @AfterReturning(returning = "ret", pointcut = "bizLog()")
    public void doAfterReturningBizLog(JoinPoint joinPoint, Object ret) throws Throwable {
        getJoinPointInfo(joinPoint, ret);
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Result result = Web.Request.getWebDoResult(request); // 优先从request中取值
        String msg = "";
        if (result == null && ret instanceof Result) { // 返回结果为json格式
            result = (Result) ret;
        }
        if (result != null) {
            msg = result.getMessage();
        }
        // 处理完请求，返回内容
        LogUtil.saveLogbyDB(request.getServletPath(), msg, request, false);
    }

    /**
     * 根据Result中的obj对象保存日志信息，返回布尔型，true表示保存成功，false表示未成功
     * 
     * @Title recordLogByResultObj
     * @author 吕凯
     * @date 2018年6月20日 上午10:32:04
     * @param request
     * @param msg
     * @param exceptionInfo
     * @param resultObj
     * @return boolean
     */
    private boolean recordLogByResultObj(HttpServletRequest request, String msg, String exceptionInfo, Object resultObj) {
        SysUser user = Web.Util.getSessionUser(request); // session中的用户不存在时才处理
        if (user == null && resultObj != null && resultObj instanceof SysUser) {// 用于退出时获取用户信息，默认session已经销毁
            SysUser resultUser = (SysUser) resultObj;
            // TODO 待细化
            LogUtil.saveLogbyDB(resultUser.getId(), resultUser.getUserName(), request.getRequestURI(), msg, request, true);
            return true;
        } else {

        }
        return false;
    }

    // 后置异常通知
    @AfterThrowing(throwing = "ex", pointcut = "commonAccessLog() && !error() && !bizLog()")
    public void throwss(JoinPoint joinPoint, Throwable ex) {
        outLog("方法异常时执行.....");
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Result result = Web.Request.getWebDoResult(request);
        String msg = "异常：" + ex.toString();
        if (result != null) {
            String exceptionInfo = "";
            StackTraceElement[] throwable = ex.getStackTrace();
            for (StackTraceElement ste : throwable) {
                exceptionInfo += "\n " + ste;
            }
            Object resultObj = result.getData();
            if (recordLogByResultObj(request, msg, exceptionInfo, resultObj)) {
                return;
            }
        }
        // 处理完请求，返回内容
        LogUtil.saveLogbyDB(request.getRequestURI(), msg, request);
    }

    // 后置最终通知,final增强，不管是抛出异常或者正常退出都会执行
//    @After("commonAccessLog()")
    public void after(JoinPoint joinPoint) {
        if (isNologMethod(joinPoint)) { // 不记录日志直接返回
            return;
        }
        outLog("方法最后执行.....");
    }

    // 环绕通知,环绕增强，相当于MethodInterceptor
//    @Around("commonAccessLog()")
    public Object around(ProceedingJoinPoint pjp) {
        outLog("方法环绕start.....");
        try {
            Object o = pjp.proceed();
            outLog("方法环绕proceed，结果是 :" + o);
            return o;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }
}